/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package nl.unimaas.games.tron.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 *
 * @author Cliff Laschet
 */
public class LGRTables {

    private HashMap lgr1p1;
    private HashMap lgr2p1;
    private HashMap lgr1p2;
    private HashMap lgr2p2;
    private boolean forgetting;
    private int boardSize;

    /**
     * 
     * @param boardSize
     * @param forgetting 
     */
    public LGRTables(int boardSize, boolean forgetting) {
        this.boardSize = boardSize;
        this.forgetting = forgetting;
        this.lgr1p1 = new HashMap(boardSize * 3);
        this.lgr2p1 = new HashMap(boardSize * 3 * 3);
        this.lgr1p2 = new HashMap(boardSize * 3);
        this.lgr2p2 = new HashMap(boardSize * 3 * 3);
    }

    /**
     * 
     */
    public void resetTables() {
        this.lgr1p1.clear();
        this.lgr2p1.clear();
        this.lgr1p2.clear();
        this.lgr2p2.clear();
    }

    /**
     * 
     * @param moveList
     * @param startingPlayer
     * @param rewardPlayer
     * @param syncMoves 
     */
    public void update(int[] moveList, int startingPlayer, int rewardPlayer, double reward, boolean syncMoves) {
        if (syncMoves) {
            updateSyncMoves(moveList, startingPlayer, rewardPlayer, reward);
        } else {
            updateASyncMoves(moveList, startingPlayer, rewardPlayer, reward);
        }
    }

    /**
     * 
     * @param moveList
     * @param startingPlayer
     * @param rewardPlayer 
     */
    public void updateSyncMoves(int[] moveList, int startingPlayer, int rewardPlayer, double reward) {
        double curReward = reward;
        int curPlayer = startingPlayer;
        HashMap curLGR1Table, curLGR2Table;
        String hash;

        if (startingPlayer != rewardPlayer) {
            curReward *= -1;
        }

        if (curPlayer == 0) {
            curLGR1Table = this.lgr1p1;
            curLGR2Table = this.lgr2p1;
        } else {
            curLGR1Table = this.lgr1p2;
            curLGR2Table = this.lgr2p2;
        }

        for (int i = 0; i < moveList.length; i++) {
            //LGR-1 part
            if ((i >= 2)) {
                int lastIndex = i - 1;
                if (i % 2 == 1) {
                    lastIndex = i - 3;
                }
                hash = getHashKey(0, moveList[lastIndex]);
                if (curLGR1Table.containsKey(hash)) {
                    int move = (Integer) curLGR1Table.get(hash);
                    if (curReward == 1) {
                        curLGR1Table.put(hash, moveList[i]);
                    } else if (curReward == -1 && move == moveList[i] && this.forgetting) {
                        curLGR1Table.remove(hash);
                    }
                } else {
                    if (curReward == 1) {
                        curLGR1Table.put(hash, moveList[i]);
                    }
                }

                //LGR-2 part
                if (i >= 4) {
                    int secondIndex = i - 1;
                    int firstIndex = i - 4;
                    if (i % 2 == 1) {
                        secondIndex = i - 3;
                        firstIndex = i - 4;
                    }
                    hash = getHashKey(moveList[firstIndex], moveList[secondIndex]);
                    if (curLGR2Table.containsKey(hash)) {
                        int move = (Integer) curLGR2Table.get(hash);
                        if (curReward == 1) {
                            curLGR2Table.put(hash, moveList[i]);
                        } else if (curReward == -1 && move == moveList[i] && this.forgetting) {
                            curLGR2Table.remove(hash);
                        }
                    } else {
                        if (curReward == 1) {
                            curLGR2Table.put(hash, moveList[i]);
                        }
                    }
                }
            }

            //Switch player, reward and hashtables.
            curPlayer = 1 - curPlayer;
            curReward *= -1;
            if (curPlayer == 0) {
                curLGR1Table = this.lgr1p1;
                curLGR2Table = this.lgr2p1;
            } else {
                curLGR1Table = this.lgr1p2;
                curLGR2Table = this.lgr2p2;
            }
        }
    }

    /**
     * 
     * @param moveList
     * @param startingPlayer
     * @param rewardPlayer 
     */
    public void updateASyncMoves(int[] moveList, int startingPlayer, int rewardPlayer, double reward) {
        double curReward = reward;
        int curPlayer = startingPlayer;
        HashMap curLGR1Table, curLGR2Table;
        String hash;

        if (startingPlayer != rewardPlayer) {
            curReward *= -1;
        }

        if (curPlayer == 0) {
            curLGR1Table = this.lgr1p1;
            curLGR2Table = this.lgr2p1;
        } else {
            curLGR1Table = this.lgr1p2;
            curLGR2Table = this.lgr2p2;
        }

        for (int i = 0; i < moveList.length; i++) {
            //LGR-1 part
            if (i > 0) {
                hash = getHashKey(0, moveList[i - 1]);
                if (curLGR1Table.containsKey(hash)) {
                    int move = (Integer) curLGR1Table.get(hash);
                    if (curReward == 1) {
                        curLGR1Table.put(hash, moveList[i]);
                    } else if (curReward == -1 && move == moveList[i] && this.forgetting) {
                        curLGR1Table.remove(hash);
                    }
                } else {
                    if (curReward == 1) {
                        curLGR1Table.put(hash, moveList[i]);
                    }
                }

                //LGR-2 part
                if (i > 1) {
                    hash = getHashKey(moveList[i - 2], moveList[i - 1]);
                    if (curLGR2Table.containsKey(hash)) {
                        int move = (Integer) curLGR2Table.get(hash);
                        if (curReward == 1) {
                            curLGR2Table.put(hash, moveList[i]);
                        } else if (curReward == -1 && move == moveList[i] && this.forgetting) {
                            curLGR2Table.remove(hash);
                        }
                    } else {
                        if (curReward == 1) {
                            curLGR2Table.put(hash, moveList[i]);
                        }
                    }
                }
            }

            //Switch player, reward and hashtables.
            curPlayer = 1 - curPlayer;
            curReward *= -1;
            if (curPlayer == 0) {
                curLGR1Table = this.lgr1p1;
                curLGR2Table = this.lgr2p1;
            } else {
                curLGR1Table = this.lgr1p2;
                curLGR2Table = this.lgr2p2;
            }
        }
    }

    /**
     * 
     * @param previousMove
     * @param validMoves
     * @return 
     */
    public int getLGR1Position(int player, int previousMove, ArrayList<Integer> validMoves) {
        Iterator<Integer> it = validMoves.iterator();
        String hash = getHashKey(0, previousMove);
        int curMove, evalMove;

        HashMap curLGR1Table;
        if (player == 0) {
            curLGR1Table = this.lgr1p1;
        } else {
            curLGR1Table = this.lgr1p2;
        }

        if (curLGR1Table.containsKey(hash)) {
            evalMove = (Integer) curLGR1Table.get(hash);
            while (it.hasNext()) {
                curMove = it.next();
                if (evalMove == curMove) {
                    return curMove;
                }
            }
        }

        //No moves found in LGR tables.
        return -1;
    }

    /**
     * 
     * @param move1
     * @param move2
     * @param validMoves
     * @return 
     */
    public int getLGR2Position(int player, int move1, int move2, ArrayList<Integer> validMoves) {
        Iterator<Integer> it = validMoves.iterator();
        int curMove, evalMove;
        String hash = getHashKey(move1, move2);

        HashMap curLGR1Table, curLGR2Table;
        if (player == 0) {
            curLGR1Table = this.lgr1p1;
            curLGR2Table = this.lgr2p1;
        } else {
            curLGR1Table = this.lgr1p2;
            curLGR2Table = this.lgr2p2;
        }

        if (curLGR2Table.containsKey(hash)) {
            evalMove = (Integer) curLGR2Table.get(hash);
            while (it.hasNext()) {
                curMove = it.next();
                if (evalMove == curMove) {
                    return curMove;
                }
            }
        }

        it = validMoves.iterator();
        hash = getHashKey(0, move2);
        if (curLGR1Table.containsKey(hash)) {
            evalMove = (Integer) curLGR1Table.get(hash);
            while (it.hasNext()) {
                curMove = it.next();
                if (evalMove == curMove) {
                    return curMove;
                }
            }
        }

        //No moves found in LGR tables.
        return -1;
    }

    /**
     * 
     * @param a
     * @param b
     * @param c
     * @return 
     */
    public String getHashKey(int a, int b) {
        StringBuilder builder = new StringBuilder(12);
        builder.append(a);
        builder.append(b);
        return builder.toString();
    }

    public double getHeuristicValue(int firstMove, int secondMove, int thirdMove) {
        return 0;
    }
}
